﻿/*
Copyright 2010 Shane Ng

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web;
using java.net;

namespace Jeans.Web {
    public class ServletModule : IHttpModule {
        private static WeakReference classLoaderRef;
        private static DateTime lastAccess = DateTime.Now;
        private static string ROOT;
        public void Dispose() {
            classLoaderRef = null;
        }

        public void Init(HttpApplication context) {
            ROOT = context.Context.Server.MapPath("~/");

            java.lang.Thread.currentThread().setContextClassLoader(GetClassLoader());
            try {
                using (var webxml = File.OpenText(Path.Combine(ROOT, @"WEB-INF\web.xml"))) {
                    using (var jspwebxml = File.OpenText(Path.Combine(ROOT, @"WEB-INF\jspweb.xml"))) {
                        ServletContextAdapter.Init(context.Context, webxml, jspwebxml);
                    }
                }
            }
            catch (Exception ex) {
                Debug.WriteLine(ex.Message);
                Debug.WriteLine(ex.StackTrace);
                throw;
            }

            context.BeginRequest += new EventHandler(context_BeginRequest);
        }

        void context_BeginRequest(object sender, EventArgs e) {
            java.lang.Thread.currentThread().setContextClassLoader(GetClassLoader());
        }

        private java.lang.ClassLoader GetClassLoader() {
            if (classLoaderRef == null || classLoaderRef.Target == null || CheckUpdated()) {
                List<URL> jarurls = new List<URL>();
                string path = Path.Combine(ROOT, "WEB-INF\\classes");
                if (Directory.Exists(path)) {
                    jarurls.Add(new java.io.File(path).toURI().toURL());
                }

                path = Path.Combine(ROOT, "WEB-INF\\lib");
                if (Directory.Exists(path)) {
                    string[] jars = Directory.GetFiles(path, "*.jar");
                    for (int i = 0; i < jars.Length; i++) {
                        jarurls.Add(new java.io.File(jars[i]).toURI().toURL());
                    }
                }

                Assembly asm = Assembly.GetExecutingAssembly();
                try {
                    asm = Assembly.Load("jsp");
                }
                catch { }
                java.lang.ClassLoader loader = new URLClassLoader(jarurls.ToArray(), new ikvm.runtime.ClassPathAssemblyClassLoader(asm));
                classLoaderRef = new WeakReference(loader);
                return loader;
            }
            else {
                return classLoaderRef.Target as java.lang.ClassLoader;
            }
        }

        private bool CheckUpdated() {
            bool result = false;
            
            result = CheckUpdated(@"WEB-INF\classes", "*.class", SearchOption.AllDirectories) |
                CheckUpdated(@"WEB-INF\lib", "*.jar") |
                CheckUpdated(@"WEB-INF\lib", "*.zip");
            return result;
        }

        private bool CheckUpdated(string path, string ext, SearchOption option = SearchOption.TopDirectoryOnly) {
            bool result = false;
            path = Path.Combine(ROOT, path);
            if (Directory.Exists(path)) {
                foreach (var f in Directory.GetFiles(path, ext, option)) {
                    var lastUpdatedFile = File.GetLastWriteTime(f);
                    if (lastUpdatedFile > lastAccess) {
                        result = true;
                        lastAccess = lastUpdatedFile;
                    }
                }
            }
            return result;
        }
    }
}
